package com.baomidou.plugin.idea.mybatisx.util;

import com.baomidou.plugin.idea.mybatisx.generate.plugin.helper.IntellijColumnInfo;
import com.baomidou.plugin.idea.mybatisx.generate.plugin.helper.IntellijTableInfo;
import com.intellij.database.model.DasColumn;
import com.intellij.database.model.DasTableKey;
import com.intellij.database.model.DasTypedObject;
import com.intellij.database.model.MultiRef;
import com.intellij.database.psi.DbTable;
import com.intellij.database.util.DasUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.JBIterable;
import com.rits.cloning.Cloner;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;

/**
 * The type Db tools utils.
 */
public class DbToolsUtils {
    private static final Cloner MY_CLONER = new Cloner();

    /**
     * Build intellij table info intellij table info.
     *
     * @param currentTable the current table
     * @return the intellij table info
     */
    public static IntellijTableInfo buildIntellijTableInfo(DbTable currentTable) {
        IntellijTableInfo tableInfo = new IntellijTableInfo();
        tableInfo.setTableName(currentTable.getName());
        tableInfo.setDatabaseType(extractDatabaseTypeFromUrl(currentTable));
        tableInfo.setTableRemark(currentTable.getComment());
        tableInfo.setTableType(currentTable.getTypeName());
        List<IntellijColumnInfo> intellijColumnInfos = new ArrayList<>();
        JBIterable<? extends DasColumn> columns = DasUtil.getColumns(currentTable);
        for (DasColumn column : columns) {
            IntellijColumnInfo columnInfo = convertColumnToIntellijColumnInfo(column, tableInfo.getDatabaseType());
            intellijColumnInfos.add(columnInfo);
        }

        tableInfo.setColumnInfos(intellijColumnInfos);
        List<IntellijColumnInfo> primaryColumnInfos = new ArrayList<>();
        DasTableKey primaryKey = DasUtil.getPrimaryKey(currentTable);
        if (primaryKey != null) {
            MultiRef<? extends DasTypedObject> columnsRef = primaryKey.getColumnsRef();
            MultiRef.It<? extends DasTypedObject> iterate = columnsRef.iterate();
            short s = 0;
            while (iterate.hasNext()) {
                String columnName = iterate.next();
                for (IntellijColumnInfo intellijColumnInfo : intellijColumnInfos) {
                    if (columnName.equals(intellijColumnInfo.getName())) {
                        IntellijColumnInfo info = MY_CLONER.deepClone(intellijColumnInfo);
                        info.setKeySeq(s);
                        primaryColumnInfos.add(info);
                        s++;
                        break;
                    }
                }
            }

        }

        tableInfo.setPrimaryKeyColumns(primaryColumnInfos);
        return tableInfo;
    }


    private static String extractDatabaseTypeFromUrl(DbTable currentTable) {
        String url = currentTable.getDataSource().getConnectionConfig().getUrl();
        return extractDatabaseTypeFromUrl(url);
    }

    /**
     * Extract database type from url string.
     *
     * @param url the url
     * @return the string
     */
    public static String extractDatabaseTypeFromUrl(String url) {
        if (url == null) {
            return "";
        } else {
            url = url.toLowerCase();
            if (url.contains(":mysql")) {
                return "MySql";
            } else if (url.contains(":oracle")) {
                return "Oracle";
            } else if (url.contains(":postgresql")) {
                return "PostgreSQL";
            } else if (url.contains(":sqlserver")) {
                return "SqlServer";
            } else {
                return url.contains(":sqlite") ? "Sqlite" : "";
            }
        }
    }

    /**
     * Convert column to intellij column info intellij column info.
     *
     * @param column       the column
     * @param databaseType the database type
     * @return the intellij column info
     */
    @NotNull
    public static IntellijColumnInfo convertColumnToIntellijColumnInfo(DasColumn column, String databaseType) {
        IntellijColumnInfo columnInfo = new IntellijColumnInfo();
        columnInfo.setName(column.getName());
        columnInfo.setDataType(convertTypeNameToJdbcType(column.getDataType().typeName, column.getDataType().size, databaseType));
        if (DasUtil.isAutoGenerated(column)) {
            columnInfo.setGeneratedColumn(true);
        }

        if (DasUtil.isAutoGenerated(column)) {
            columnInfo.setAutoIncrement(true);
        }

        columnInfo.setSize(column.getDataType().getLength());
        columnInfo.setDecimalDigits(column.getDataType().getScale());
        columnInfo.setRemarks(column.getComment());
        columnInfo.setColumnDefaultValue(column.getDefault());
        columnInfo.setNullable(!column.isNotNull());
        columnInfo.setKeySeq(column.getPosition());

        return columnInfo;
    }

    /**
     * Convert type name to jdbc type int.
     *
     * @param jdbcTypeName the jdbc type name
     * @param size         the size
     * @param databaseType the database type
     * @return the int
     */
//这个我也不知道是什么，复制别人的代码
    public static int convertTypeNameToJdbcType(String jdbcTypeName, int size, String databaseType) {
        if (StringUtil.isEmpty(jdbcTypeName)) {
            return 1111;
        } else {
            String fixed = jdbcTypeName.toUpperCase();
            if (fixed.contains("BIGINT")) {
                return -5;
            } else if (fixed.contains("TINYINT")) {
                return size == 1 ? 16 : -6;
            } else if (fixed.contains("LONGVARBINARY")) {
                return -4;
            } else if (fixed.contains("VARBINARY")) {
                return -3;
            } else if (fixed.contains("LONGVARCHAR")) {
                return -1;
            } else if (fixed.contains("SMALLINT")) {
                return 5;
            } else if (fixed.contains("DATETIME")) {
                return 93;
            } else if ("DATE".equals(fixed) && "Oracle".equals(databaseType)) {
                return 93;
            } else if (fixed.contains("NUMBER")) {
                return 3;
            } else if (fixed.contains("BOOLEAN")) {
                return 16;
            } else if (fixed.contains("BINARY")) {
                return -3;
            } else if (fixed.contains("BIT")) {
                return -7;
            } else if (fixed.contains("BOOL")) {
                return 16;
            } else if (fixed.contains("DATE")) {
                return 91;
            } else if (fixed.contains("TIMESTAMP")) {
                return 93;
            } else if (fixed.contains("TIME")) {
                return 92;
            } else if (!fixed.contains("REAL") && !fixed.contains("NUMBER")) {
                if (fixed.contains("FLOAT")) {
                    return 6;
                } else if (fixed.contains("DOUBLE")) {
                    return 8;
                } else if ("CHAR".equals(fixed)) {
                    return 1;
                } else if (fixed.contains("INT") && !fixed.contains("INTERVAL")) {
                    return 4;
                } else if (fixed.contains("DECIMAL")) {
                    return 3;
                } else if (fixed.contains("NUMERIC")) {
                    return 2;
                } else if (!fixed.contains("CHAR") && !fixed.contains("TEXT")) {
                    if (fixed.contains("BLOB")) {
                        return 2004;
                    } else if (fixed.contains("CLOB")) {
                        return 2005;
                    } else {
                        return fixed.contains("REFERENCE") ? 2006 : 1111;
                    }
                } else {
                    return 12;
                }
            } else {
                return 7;
            }
        }
    }

}
